package com.yuzf.netty.example.nio.chat.server;

import com.sun.org.apache.bcel.internal.generic.DDIV;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Set;

/**
 * @ClassName ChatServer
 * @Description: 服务器端
 * @Author yuzf
 * @Date 2020/6/23
 * @Version V1.0
 **/
@Slf4j
public class ChatServer {
    public static final Integer DEFAULT_PORT = 12801;
    public static final String QUIT = "quit";
    public static final Integer DEFAULT_BUFFER_SIZE = 1024;

    private ServerSocketChannel serverSocketChannel;
    private ByteBuffer rbuffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
    private ByteBuffer wbuffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
    private Charset charset = Charset.forName("UTF-8");
    private Selector selector;
    private Integer port;

    public ChatServer() {
        this(DEFAULT_PORT);
    }

    public ChatServer(Integer port) {
        this.port = port;
    }

    public void start() {
        try (
                ServerSocketChannel ssc = ServerSocketChannel.open();
                Selector selec = Selector.open();

        ) {
            serverSocketChannel = ssc;
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress(this.port));
            selector = selec;
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            log.info("启动服务器，监听端口:{}", this.port);
            while (true) {
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                for (SelectionKey key : selectionKeys) {
                    handle(key);
                }
                selectionKeys.clear();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void handle(SelectionKey key) throws IOException {
        //Accept连接
        if (key.isAcceptable()) {
            ServerSocketChannel server = (ServerSocketChannel) key.channel();
            SocketChannel client = server.accept();
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ);
            log.info("客户端[{}]已连接!!!", client.socket().getPort());
        } else if (key.isReadable()) {
            //read读取消息
            SocketChannel client = (SocketChannel) key.channel();
            String fwdMsg = receive(client);
            if(fwdMsg.isEmpty()){
                key.cancel();
                selector.wakeup();
            }else {
                forwardMessage(client, fwdMsg);
                log.info("客户端[{}]:{}",client.socket().getPort(), fwdMsg);
                if(readyToQuit(fwdMsg)){
                    key.cancel();
                    selector.wakeup();
                    log.info("客户端[{}]已退出连接", client.socket().getPort());
                }
            }
        }

    }

    private void forwardMessage(SocketChannel client, String fwdMsg) throws IOException {
        for (SelectionKey key: selector.keys()){
            if(key.channel() instanceof ServerSocketChannel){
                continue;
            }
            if(key.isValid() && !client.equals(key.channel())){
                wbuffer.clear();
                wbuffer.put(charset.encode("客户端["+client.socket().getPort()+"]:"+fwdMsg));
                wbuffer.flip();
                SocketChannel channel = (SocketChannel) key.channel();
                while (wbuffer.hasRemaining()){
//                    (SocketChannel)(key.channel()).write(wbuffer);
                    channel.write(wbuffer);
                }

            }
        }
    }

    private String receive(SocketChannel client) throws IOException {
        rbuffer.clear();
        while (client.read(rbuffer)>0);
        rbuffer.flip();
        return String.valueOf(charset.decode(rbuffer));
    }

    private boolean readyToQuit(String fwdMsg) {
        return QUIT.equals(fwdMsg);
    }

    public static void main(String[] args) {
        ChatServer chatServer = new ChatServer();
        chatServer.start();
    }
}
